/**   
 * @Title: Base64.java 
 * @Package com.rsatest 
 * @Description: BASE64工具类 
 * @author Miko baoz@500wan.com   
 * @date 2011-5-23 下午03:05:21  
 * copy right 深圳市易讯天空网络技术有限公司
 */
package com.esun.util.encryption;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;

/**
 * @ClassName: Base64
 * @Description: BASE64工具类
 * @author Miko baoz@500wan.com
 * @date 2011-5-23 下午03:05:21
 * 
 */
public class Base64 {

	public static String base64Encoder(byte[] bytes) {
		StringBuilder result = new StringBuilder();
		String base64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
		// prevByteBitCount表示从前一个字节取得的bit数，nextByteBitCount表示从后一个字节取得的bit数
		int prevByteBitCount = 0, nextByteBitCount = 6;
		// i表示当前的数组索引，n表示已经处理的位数
		int i = 0, n = 0;
		// byteCount表示总的位数
		int byteCount = 8 * bytes.length;
		byte b = 0;
		while (true) {
			// 处理从前后两个字节取得位数的情况
			if (prevByteBitCount > 0 && nextByteBitCount > 0) {
				// 将前一个字节的低位向左移nextByteBitCount个bit，并使下一个字节的高位（nextByteBitCount指定的位数）右移到字节的最低位，
				// 然后将两个位移结果进行逻辑或，也就是将从前一个字节和后一个字节取得的相应的bit合并为一个字节的低位
				b = (byte) (((0xff & bytes[i]) << nextByteBitCount) | ((0xff & bytes[i + 1]) >> (8 - nextByteBitCount)));
				// 将逻辑或后的结果的最高两个bit置成0
				b = (byte) (b & 0x3f);
				prevByteBitCount = 8 - nextByteBitCount;
				nextByteBitCount = 6 - prevByteBitCount;
			}
			// 处理从后一个字节取得高6位的情况
			else if (prevByteBitCount == 0) {
				// 后一个字节的高6位右移动低6位
				b = (byte) ((0xff & bytes[i]) >> (8 - nextByteBitCount));
				// 处理后面的位时，就是从前一个字节取2个bit，从后一个字字取4个bit
				prevByteBitCount = 2;
				nextByteBitCount = 4;

			}
			// 处理从前一个字节取得低6位的情况
			else if (nextByteBitCount == 0) {
				// 将前一个字节的最高两个bit置成0
				b = (byte) (0x3f & bytes[i]);
				// 处理后面的位时，从后一个字节取6个bit
				prevByteBitCount = 0;
				nextByteBitCount = 6;

			}
			result.append(base64.charAt(b));
			n += 6;
			i = n / 8;
			int remainBitCount = byteCount - n;
			if (remainBitCount < 6) {
				// 将剩余的bit补0后，仍然需要在base64编码表中查找相应的字符，并添加到结果字符串的最后
				if (remainBitCount > 0) {
					b = bytes[bytes.length - 1];
					b = (byte) (0x3f & (b << (6 - remainBitCount)));
					result.append(base64.charAt(b));
				}
				break;
			}
		}
		// 如果总bit数除3的余数为1，加一个“=”，为2，加两个“=”
		n = byteCount % 3;
		for (i = 0; i < n; i++)
			result.append("=");

		return result.toString();
	}
	
	
	/**
     * Decodes the given Base64 encoded String to a new byte array. The byte
     * array holding the decoded data is returned.
     */

    public static byte[] decode(String s) {

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        try {
            decode(s, bos);
        } catch (IOException e) {
            throw new RuntimeException();
        }
        byte[] decodedBytes = bos.toByteArray();
        try {
            bos.close();
            bos = null;
        } catch (IOException ex) {
            System.err.println("Error while decoding BASE64: " + ex.toString());
        }
        return decodedBytes;
    }
    
    
    private static int decode(char c) {
        if (c >= 'A' && c <= 'Z')
            return ((int) c) - 65;
        else if (c >= 'a' && c <= 'z')
            return ((int) c) - 97 + 26;
        else if (c >= '0' && c <= '9')
            return ((int) c) - 48 + 26 + 26;
        else
            switch (c) {
            case '+':
                return 62;
            case '/':
                return 63;
            case '=':
                return 0;
            default:
                throw new RuntimeException("unexpected code: " + c);
            }
    }

    private static void decode(String s, OutputStream os) throws IOException {
        int i = 0;

        int len = s.length();

        while (true) {
            while (i < len && s.charAt(i) <= ' ')
                i++;

            if (i == len)
                break;

            int tri = (decode(s.charAt(i)) << 18)
                    + (decode(s.charAt(i + 1)) << 12)
                    + (decode(s.charAt(i + 2)) << 6)
                    + (decode(s.charAt(i + 3)));

            os.write((tri >> 16) & 255);
            if (s.charAt(i + 2) == '=')
                break;
            os.write((tri >> 8) & 255);
            if (s.charAt(i + 3) == '=')
                break;
            os.write(tri & 255);

            i += 4;
        }
    }
}
